﻿using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Text;

namespace DotNetClient
{
    /*
     * 0     1     2     3     4     5
     * +-----+-----+-----+-----+-----+
     * |        length         | type|
     * +-----------+-----+-----+-----+
     * |   route   |  id |           |
     * +-----------+-----+           +
     * |         ... body ...        |
     * +-----------------------------+
     * 
     * Header
     * length: 消息长度(type + (route + id + body))
     * type: 消息类型 msg=1 handshake=2 heartbeat=3
     * 
     * Message
     * route: 路由索引
     * id: 请求唯一ID
     * body: 参数 prototbuf编码
     */

    public class MessageProtocol
    {
        public const int HEADER_LENGTH = 3;
        public const int MSG_ROUTE_LIMIT = 255;

        private List<string> _route = new List<string>(); // 路由数组

        public MessageProtocol(string[] route)
        {
            for (int i = 0; i < route.Length; i++)
            {
                _route.Add(route[i]);
            }
        }

        public byte[] encode<T>(string route, T msg) where T : IMessage
        {
            return encode(route, 0, msg);
        }

        public byte[] encode<T>(string route, ushort id, T msg) where T : IMessage
        {
            int routeLength = byteLength(route);
            if (routeLength > MSG_ROUTE_LIMIT)
            {
                throw new Exception("Route is too long! -> " + route);
            }

            int routeIndex = _route.IndexOf(route);
            if (routeIndex == -1)
            {
                throw new Exception("Route is not exists! -> " + route);
            }

            byte[] msgBytes = msg.ToByteArray();
            int length = msgBytes.Length + HEADER_LENGTH;

            byte[] bytes = new byte[length];
            int index = 0;
            bytes[index++] = Convert.ToByte(routeIndex >> 8 & 0xFF);
            bytes[index++] = Convert.ToByte(routeIndex & 0xFF);
            bytes[index++] = Convert.ToByte(id & 0xFF);

            while (index < length)
            {
                bytes[index] = msgBytes[index - HEADER_LENGTH];
                index++;
            }

            return bytes;
        }

        public Message decode(byte[] buffer)
        {
            int routeIndex = buffer[0] << 8 | buffer[1];
            byte id = buffer[2];
            string route = _route[routeIndex];

            // Decode body
            byte[] body = new byte[buffer.Length - HEADER_LENGTH];
            for (int i = 0; i < body.Length; i++)
            {
                body[i] = buffer[i + HEADER_LENGTH];
            }

            // Construct the message
            MessageType messageType = id > 0 ? MessageType.MSG_RESPONSE : MessageType.MSG_PUSH;
            return new Message(messageType, route, id, body);
        }

        private int byteLength(string msg)
        {
            return Encoding.UTF8.GetBytes(msg).Length;
        }
    }
}
